﻿using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace PIMS.YongXiang
{
    public class RSAHelper
    {
        //参考连接：https://www.jianshu.com/p/faefcc58c79b
        //工具网址1：https://the-x.cn/certificate/PemToXml.aspx
        //工具网址2：https://superdry.apphb.com/tools/online-rsa-key-converter
        //需要依赖一个第三方库，叫BouncyCastle ；在线获取安装包的代码是：PM >Install-Package BouncyCastle
        //安装步骤：VS中选择  工具(T)---》NuGet包管理器(N)---》程序包管理器控制台(O)---》然后数据以上代码即可

        #region Demo示例
        public static void Demo()
        {
            string xmlgy = "";
            string xmlsy = "";
            string xmlmdata = "";
            string xmljdata = "";
            string xmlsign = "";
            string xmlresult = "";
            string pemgy = "";
            string pemsy = "";
            string pemmdata = "";
            string pemjdata = "";
            string pemsign = "";
            string pemresult = "";
            RSAHelper.GetXMLRSAKeys(out xmlsy, out xmlgy);
            RSAHelper.GetPEMRSAKeys(out pemsy, out pemgy);
            string str1 = "1234567890";
            Console.WriteLine("===================xml===================");
            Console.WriteLine("===================xml公钥");
            Console.WriteLine(xmlgy);
            Console.WriteLine("===================xml私钥");
            Console.WriteLine(xmlsy);
            Console.WriteLine("===================xml公钥加密后的数据");
            xmlmdata = RSAHelper.XMLRSAEncrypt(xmlgy, str1);
            Console.WriteLine(xmlmdata);
            Console.WriteLine("===================xml私钥解密后的字符");
            xmljdata = RSAHelper.XMLRSADecrypt(xmlsy, xmlmdata);
            Console.WriteLine(xmljdata);
            Console.WriteLine("===================xml私钥加签后的sign");
            xmlsign = RSAHelper.XMLSign(xmlmdata, xmlsy);
            Console.WriteLine(xmlsign);
            Console.WriteLine("===================xml公钥验签后的结果");
            xmlresult = RSAHelper.XMLSignCheck(xmlmdata, xmlsign, xmlgy).ToString();
            Console.WriteLine(xmlresult);
            Console.WriteLine("===================xml===================");
            Console.WriteLine("=========================================*****=========================================");
            Console.WriteLine("===================pem===================");
            Console.WriteLine("===================pem公钥");
            Console.WriteLine(pemgy);
            Console.WriteLine("===================pem私钥");
            Console.WriteLine(pemsy);
            Console.WriteLine("===================pem公钥加密后的数据");
            pemmdata = RSAHelper.PEMRSAEncrypt(pemgy, str1);
            Console.WriteLine(pemmdata);
            Console.WriteLine("===================pem私钥解密后的字符");
            pemjdata = RSAHelper.PEMRSADecrypt(pemsy, pemmdata);
            Console.WriteLine(pemjdata);
            Console.WriteLine("===================pem私钥加签后的sign");
            pemsign = RSAHelper.PEMSign(pemmdata, pemsy);
            Console.WriteLine(pemsign);
            Console.WriteLine("===================pem公钥验签后的结果");
            pemresult = RSAHelper.PEMSignCheck(pemmdata, pemsign, pemgy).ToString();
            Console.WriteLine(pemresult);
            Console.WriteLine("===================pem===================");
        }
        #endregion

        #region 获取RSA密钥对
        /// <summary>
        /// 获取XML格式密钥对
        /// </summary>
        /// <param name="xmlprikey">xml私钥</param>
        /// <param name="xmlpubkey">xml公钥</param>
        public static void GetXMLRSAKeys(out string xmlprikey, out string xmlpubkey)
        {
            try
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                xmlprikey = rsa.ToXmlString(true);
                xmlpubkey = rsa.ToXmlString(false);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取PEM格式密钥对
        /// </summary>
        /// <param name="pemprikey">pem私钥</param>
        /// <param name="pempubkey">pem公钥</param>
        public static void GetPEMRSAKeys(out string pemprikey, out string pempubkey)
        {
            try
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                pemprikey = XMLToPEM_Pri(rsa.ToXmlString(true));
                pempubkey = XMLToPEM_Pub(rsa.ToXmlString(false));

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region RSA加密
        /// <summary>
        /// 使用XML格式加密
        /// </summary>
        /// <param name="xmlpubkey">xml公钥</param>
        /// <param name="encryptstr">需要加密的字符串</param>
        /// <returns>返回加密后的字符串</returns>
        public static string XMLRSAEncrypt(string xmlpubkey, string encryptstr)
        {
          
                byte[] PlainTextBArray;
                byte[] CypherTextBArray;
                string Result;
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(xmlpubkey);
                PlainTextBArray = (new UTF8Encoding()).GetBytes(encryptstr);
                CypherTextBArray = rsa.Encrypt(PlainTextBArray, false);
                Result = Convert.ToBase64String(CypherTextBArray);
                return Result;
            
        }
        /// <summary>
        /// 使用PEM格式加密
        /// </summary>
        /// <param name="pempubkey">pem公钥</param>
        /// <param name="encryptstr">需要加密的字符串</param>
        /// <returns>返回加密后的字符串</returns>
        public static string PEMRSAEncrypt(string pempubkey, string encryptstr)
        {
            StringReader sr = new StringReader(pempubkey);
            AsymmetricKeyParameter publickey = new PemReader(sr).ReadObject() as AsymmetricKeyParameter;

            if (publickey == null)
                throw new Exception("PEM格式公钥不能为空");

            try
            {
                var engine = new Pkcs1Encoding(new RsaEngine());
                engine.Init(true, publickey);


                byte[] bytes = Encoding.UTF8.GetBytes(encryptstr);//Encoding.UTF8.GetBytes(data);
                bytes = engine.ProcessBlock(bytes, 0, bytes.Length);

                return Convert.ToBase64String(bytes);// Convert.ToBase64String(bytes);
            }
            catch
            {
                throw new Exception("加密失败");
            }
        }
        #endregion

        #region RSA解密
        /// <summary>
        /// 使用XML格式解密
        /// </summary>
        /// <param name="xmlprikey">xml格式私钥</param>
        /// <param name="decryptstr">需要解密的字符串</param>
        /// <returns>返回解密后的字符串</returns>
        public static string XMLRSADecrypt(string xmlprikey, string decryptstr)
        {
            try
            {
                byte[] PlainTextBArray;
                byte[] DypherTextBArray;
                string Result;
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(xmlprikey);
                PlainTextBArray = Convert.FromBase64String(decryptstr);
                DypherTextBArray = rsa.Decrypt(PlainTextBArray, false);
                Result = (new UTF8Encoding()).GetString(DypherTextBArray);
                return Result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 使用PEM格式解密
        /// </summary>
        /// <param name="pemprikey">pem格式私钥</param>
        /// <param name="decryptstr">需要解密的字符串</param>
        /// <returns>返回解密后的字符串</returns>
        public static string PEMRSADecrypt(string pemprikey, string decryptstr)
        {
            byte[] bytes = Convert.FromBase64String(decryptstr);// Convert.FromBase64String(data);
            StringReader sr = new StringReader(pemprikey);
            AsymmetricKeyParameter prikey = new PemReader(sr).ReadObject() as AsymmetricKeyParameter;

            if (prikey == null)
                throw new Exception("私钥读取失败");

            try
            {
                var engine = new Pkcs1Encoding(new RsaEngine());
                engine.Init(false, prikey);
                bytes = engine.ProcessBlock(bytes, 0, bytes.Length);

                return Encoding.UTF8.GetString(bytes);////Encoding.UTF8.GetString(bytes);
            }
            catch
            {
                throw new Exception("解密失败");
            }
        }
        #endregion

        #region RSA签名
        /// <summary>
        /// 使用XML格式的私钥加签
        /// </summary>
        /// <param name="str">需要加签的字符串</param>
        /// <param name="xmlprikey">xml格式的私钥</param>
        /// <returns>返回签名</returns>
        public static string XMLSign(string str, string xmlprikey)
        {
            //根据需要加签时的哈希算法转化成对应的hash字符节
            byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str);
            var sha256 = new SHA256CryptoServiceProvider();
            byte[] rgbHash = sha256.ComputeHash(bt);

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            key.FromXmlString(xmlprikey);
            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            formatter.SetHashAlgorithm("SHA256");                                            //此处是你需要加签的hash算法，需要和上边你计算的hash值的算法一致，不然会报错。
            byte[] inArray = formatter.CreateSignature(rgbHash);
            return Convert.ToBase64String(inArray);
        }
        /// <summary>
        /// 使用PEM格式的私钥加签
        /// </summary>
        /// <param name="str">需要加签的字符串</param>
        /// <param name="pemprikey">pem格式的私钥</param>
        /// <returns>返回签名</returns>
        public static string PEMSign(string str, string pemprikey)
        {
            pemprikey = PEMToXML_All(pemprikey, true);
            //根据需要加签时的哈希算法转化成对应的hash字符节
            byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str);
            var sha256 = new SHA256CryptoServiceProvider();
            byte[] rgbHash = sha256.ComputeHash(bt);

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            key.FromXmlString(pemprikey);
            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            formatter.SetHashAlgorithm("SHA256");                                            //此处是你需要加签的hash算法，需要和上边你计算的hash值的算法一致，不然会报错。
            byte[] inArray = formatter.CreateSignature(rgbHash);
            return Convert.ToBase64String(inArray);
        }
        #endregion

        #region RSA验签
        /// <summary>
        /// 使用XML格式的公钥验签
        /// </summary>
        /// <param name="str">需要验签的字符串</param>
        /// <param name="sign">签名</param>
        /// <param name="xmlpubkey">xml格式的公钥</param>
        /// <returns>是/否</returns>
        public static bool XMLSignCheck(string str, string sign, string xmlpubkey)
        {
            try
            {
                byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str);
                var sha256 = new SHA256CryptoServiceProvider();
                byte[] rgbHash = sha256.ComputeHash(bt);

                RSACryptoServiceProvider key = new RSACryptoServiceProvider();
                key.FromXmlString(xmlpubkey);
                RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key);
                deformatter.SetHashAlgorithm("SHA256");
                byte[] rgbSignature = Convert.FromBase64String(sign);
                if (deformatter.VerifySignature(rgbHash, rgbSignature))
                {
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 使用XML格式的公钥验签
        /// </summary>
        /// <param name="str">需要验签的字符串</param>
        /// <param name="sign">签名</param>
        /// <param name="pempubkey">pem格式的公钥</param>
        /// <returns>是/否</returns>
        public static bool PEMSignCheck(string str, string sign, string pempubkey)
        {
            try
            {
                pempubkey = PEMToXML_All(pempubkey, false);
                byte[] bt = Encoding.GetEncoding("utf-8").GetBytes(str);
                var sha256 = new SHA256CryptoServiceProvider();
                byte[] rgbHash = sha256.ComputeHash(bt);

                RSACryptoServiceProvider key = new RSACryptoServiceProvider();
                key.FromXmlString(pempubkey);
                RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key);
                deformatter.SetHashAlgorithm("SHA256");
                byte[] rgbSignature = Convert.FromBase64String(sign);
                if (deformatter.VerifySignature(rgbHash, rgbSignature))
                {
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        #endregion

        #region XML转PEM
        /// <summary>
        /// XML格式的公钥转PEM格式的公钥
        /// </summary>
        /// <param name="xmlpubkey">xml格式的公钥</param>
        /// <returns>pem格式的公钥</returns>
        public static string XMLToPEM_Pub(string xmlpubkey)
        {
            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(xmlpubkey);
            var p = rsa.ExportParameters(false);
            RsaKeyParameters key = new RsaKeyParameters(false, new BigInteger(1, p.Modulus), new BigInteger(1, p.Exponent));
            SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(key);
            byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
            string publicKey = Convert.ToBase64String(serializedPublicBytes);
            return Format(publicKey, true);
        }
        /// <summary>
        /// XML格式的私钥转PEM格式的私钥
        /// </summary>
        /// <param name="xmlprikey">xml格式的私钥</param>
        /// <returns>pem格式的私钥</returns>
        public static string XMLToPEM_Pri(string xmlprikey)
        {
            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(xmlprikey);
            var p = rsa.ExportParameters(true);
            var key = new RsaPrivateCrtKeyParameters(
                new BigInteger(1, p.Modulus), new BigInteger(1, p.Exponent), new BigInteger(1, p.D),
                new BigInteger(1, p.P), new BigInteger(1, p.Q), new BigInteger(1, p.DP), new BigInteger(1, p.DQ),
                new BigInteger(1, p.InverseQ));
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(key);
            byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
            string privateKey = Convert.ToBase64String(serializedPrivateBytes);
            return Format(privateKey, false);
        }
        /// <summary>
        /// 格式化公钥/私钥
        /// </summary>
        /// <param name="key">生成的公钥/私钥</param>
        /// <param name="type">true:公钥 false:私钥</param>
        /// <returns>PEM格式的公钥/私钥</returns>
        private static string Format(string key, bool type)
        {
            string result = string.Empty;

            int length = key.Length / 64;
            for (int i = 0; i < length; i++)
            {
                int start = i * 64;
                result = result + key.Substring(start, 64) + "\r\n";
            }

            result = result + key.Substring(length * 64);
            //if (type)
            //{
            //    result = result.Insert(0, "-----BEGIN PUBLIC KEY-----\r\n");
            //    result += "\r\n-----END PUBLIC KEY-----";
            //}
            //else
            //{
            //    result = result.Insert(0, "-----BEGIN PRIVATE KEY-----\r\n");
            //    result += "\r\n-----END PRIVATE KEY-----";
            //}

            return result;
        }
        #endregion

        #region PEM转XML
        /// <summary>
        /// PEM格式的密钥转XML格式
        /// </summary>
        /// <param name="pemkey">pem格式的密钥</param>
        /// <param name="isprikey">true：私钥;false：公钥</param>
        /// <returns>xml格式密钥</returns>
        public static string PEMToXML_All(string pemkey, bool isprikey)
        {
            if (isprikey)
            {
                pemkey = pemkey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "");
            }
            else
            {
                pemkey = pemkey.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
            }
            string rsaKey = string.Empty;
            object pemObject = null;
            RSAParameters rsaPara = new RSAParameters();
            using (StringReader sReader = new StringReader(pemkey))
            {
                var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sReader);
                pemObject = pemReader.ReadObject();
            }
            //RSA私钥
            if (isprikey)
            {
                RsaPrivateCrtKeyParameters key = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(pemkey));
                rsaPara = new RSAParameters
                {
                    Modulus = key.Modulus.ToByteArrayUnsigned(),
                    Exponent = key.PublicExponent.ToByteArrayUnsigned(),
                    D = key.Exponent.ToByteArrayUnsigned(),
                    P = key.P.ToByteArrayUnsigned(),
                    Q = key.Q.ToByteArrayUnsigned(),
                    DP = key.DP.ToByteArrayUnsigned(),
                    DQ = key.DQ.ToByteArrayUnsigned(),
                    InverseQ = key.QInv.ToByteArrayUnsigned(),
                };
            }
            //RSA公钥
            else
            {
                RsaKeyParameters key = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(pemkey));
                rsaPara.Modulus = key.Modulus.ToByteArrayUnsigned();
                rsaPara.Exponent = key.Exponent.ToByteArrayUnsigned();
            }
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(rsaPara);
            using (StringWriter sw = new StringWriter())
            {
                sw.Write(rsa.ToXmlString(isprikey ? true : false));
                rsaKey = sw.ToString();
            }
            return rsaKey;
        }
        #endregion
    }
}